package com.jacars.biz.user.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jacars.biz.user.dao.mapper.JsptRoleMenuMapper;
import com.jacars.biz.user.dao.model.JsptRoleUser;
import com.jacars.biz.user.dao.model.JsptUserExcel;
import com.jacars.biz.user.dao.service.JsptDeptService;
import com.jacars.biz.user.dao.service.JsptRoleUserService;
import com.jacars.biz.user.dao.service.JsptUserService;
import com.jacars.biz.user.service.UserService;
import com.jacars.biz.user.vo.JsptDeptVO;
import com.jacars.common.api.PageResult;
import com.jacars.common.api.Result;
import com.jacars.common.constant.CommonConstant;
import com.jacars.common.context.TenantContextHolder;
import com.jacars.common.lock.DistributedLock;
import com.jacars.common.model.*;
import com.jacars.common.utils.FuncUtil;
import com.jacars.common.utils.node.ForestNodeMerger;
import com.jacars.common.utils.node.INode;
import com.jacars.db.support.Condition;
import com.jacars.db.support.Query;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 作者 owen E-mail: 624191343@qq.com
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {
    private final static String LOCK_KEY_USERNAME = "username:";

    @Autowired
    private JsptUserService jsptUserService;

    @Autowired
    private JsptDeptService jsptDeptService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Resource
    private JsptRoleUserService jsptRoleUserService;

    @Resource
    private JsptRoleMenuMapper roleMenuMapper;

    @Autowired
    private DistributedLock lock;

    @Override
    public LoginAppUser findByUsername(String username) {
        JsptUser jsptUser = this.selectByUsername(username);
        return getLoginAppUser(jsptUser);
    }

    @Override
    public LoginAppUser findByUserId(String userId) {
        JsptUser jsptUser = this.selectByUserId(userId);
        return getLoginAppUser(jsptUser);
    }

    @Override
    public LoginAppUser findByOpenId(String username) {
        JsptUser jsptUser = this.selectByOpenId(username);
        return getLoginAppUser(jsptUser);
    }

    @Override
    public LoginAppUser findByMobile(String username) {
        JsptUser jsptUser = this.selectByMobile(username);
        return getLoginAppUser(jsptUser);
    }

    @Override
    public LoginAppUser getLoginAppUser(JsptUser jsptUser) {
        if (jsptUser != null) {
            LoginAppUser loginAppUser = new LoginAppUser();
            BeanUtils.copyProperties(jsptUser, loginAppUser);

            List<JsptRole> jsptRoles = jsptRoleUserService.findRolesByUserId(jsptUser.getId());
            // 设置角色
            loginAppUser.setRoles(jsptRoles);
            loginAppUser.setRoleId(FuncUtil.join(jsptRoles.stream().map(k -> FuncUtil.toStr(k.getId())).collect(Collectors.toSet())));

            if (!CollectionUtils.isEmpty(jsptRoles)) {
                Set<Long> roleIds = jsptRoles.stream().map(SuperEntity::getId).collect(Collectors.toSet());
                List<JsptMenu> menus = roleMenuMapper.findMenusByRoleIds(roleIds, CommonConstant.PERMISSION);
                if (!CollectionUtils.isEmpty(menus)) {
                    Set<String> permissions = menus.stream().map(p -> p.getPath())
                            .collect(Collectors.toSet());
                    // 设置权限集合
                    loginAppUser.setPermissions(permissions);
                }
            }
            return loginAppUser;
        }
        return null;
    }

    /**
     * 根据用户名查询用户
     *
     * @param username
     * @return
     */
    @Override
    public JsptUser selectByUsername(String username) {
        List<JsptUser> users = jsptUserService.list(
                new QueryWrapper<JsptUser>().eq("username", username)
        );
        return getUser(users);
    }

    /**
     * 根据userId查询用户
     *
     * @param userId
     * @return
     */
    @Override
    public JsptUser selectByUserId(String userId) {
        List<JsptUser> users = jsptUserService.list(
                new QueryWrapper<JsptUser>().eq("id", userId)
        );
        return getUser(users);
    }

    /**
     * 根据手机号查询用户
     *
     * @param mobile
     * @return
     */
    @Override
    public JsptUser selectByMobile(String mobile) {
        List<JsptUser> users = jsptUserService.list(
                new QueryWrapper<JsptUser>().eq("mobile", mobile)
        );
        return getUser(users);
    }

    /**
     * 根据openId查询用户
     *
     * @param openId
     * @return
     */
    @Override
    public JsptUser selectByOpenId(String openId) {
        List<JsptUser> users = jsptUserService.list(
                new QueryWrapper<JsptUser>().eq("open_id", openId)
        );
        return getUser(users);
    }

    private JsptUser getUser(List<JsptUser> users) {
        JsptUser user = null;
        if (users != null && !users.isEmpty()) {
            user = users.get(0);
        }
        return user;
    }

    /**
     * 给用户设置角色
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setRoleToUser(Long id, Set<Long> roleIds) {
        JsptUser jsptUser = jsptUserService.getById(id);
        if (jsptUser == null) {
            throw new IllegalArgumentException("用户不存在");
        }

        jsptRoleUserService.deleteUserRole(id, null);
        if (!CollectionUtils.isEmpty(roleIds)) {
            List<JsptRoleUser> roleUsers = new ArrayList<>(roleIds.size());
            roleIds.forEach(roleId -> roleUsers.add(new JsptRoleUser(id, roleId)));
            jsptRoleUserService.saveBatch(roleUsers);
        }
    }

    @Transactional
    @Override
    public Result updatePassword(Long id, String oldPassword, String newPassword) {
        JsptUser jsptUser = jsptUserService.getById(id);
        if (StrUtil.isNotBlank(oldPassword)) {
            if (!passwordEncoder.matches(oldPassword, jsptUser.getPassword())) {
                return Result.failed("旧密码错误");
            }
        }
        if (StrUtil.isBlank(newPassword)) {
            newPassword = CommonConstant.DEF_USER_PASSWORD;
        }
        JsptUser user = new JsptUser();
        user.setId(id);
        user.setPassword(passwordEncoder.encode(newPassword));
        jsptUserService.updateById(user);
        return Result.succeed("修改成功");
    }

    @Override
    public PageResult<JsptUser> findUsers(Map<String, Object> params, Query query) {
        //先移除deptId，保证自动生成的sql语句中不带deptId字段
        String deptId = (String)params.remove("deptId");
        QueryWrapper<JsptUser> queryWrapper = Condition.getQueryWrapper(params, JsptUser.class);
        String tenantId = TenantContextHolder.getTenant();
        List<JsptDeptVO> deptTree = jsptDeptService.tree(tenantId);
        if (FuncUtil.notNull(deptId)) {
            //查找指定deptId的下级部门
            List<INode> children = ForestNodeMerger.findChildren(deptTree, FuncUtil.toLong(deptId));
            //获取改部门和他的下级部门编号
            List<Long> childrenIds = ForestNodeMerger.findChildrenIds(children);
            queryWrapper.lambda().in(JsptUser::getDeptId, childrenIds);
        }

        IPage<JsptUser> userIPage = jsptUserService.page(Condition.getPage(query), queryWrapper);
        List<JsptUser> jsptUserList = userIPage.getRecords();
        long total = userIPage.getTotal();
        if (total > 0) {
            List<Long> userIds = jsptUserList.stream().map(JsptUser::getId).collect(Collectors.toList());
            List<JsptRole> jsptRoles = jsptRoleUserService.findRolesByUserIds(userIds);
            jsptUserList.forEach(u -> u.setRoles(jsptRoles.stream().filter(r -> Objects.equals(u.getId(), r.getUserId()))
                    .collect(Collectors.toList())));

            jsptUserList.forEach(u -> u.setRoleId(FuncUtil.join(jsptRoles.stream()
                    .filter(r -> Objects.equals(u.getId(), r.getUserId()))
                    .map(k -> FuncUtil.toStr(k.getId()))
                    .collect(Collectors.toList()))));
        }
        return PageResult.succeed(jsptUserList, total);
    }

    @Override
    public List<JsptRole> findRolesByUserId(Long userId) {
        return jsptRoleUserService.findRolesByUserId(userId);
    }

    @Override
    public Result updateEnabled(Map<String, Object> params) {
        Long id = MapUtils.getLong(params, "id");
        Boolean enabled = MapUtils.getBoolean(params, "enabled");

        JsptUser appUser = jsptUserService.getById(id);
        if (appUser == null) {
            return Result.failed("用户不存在");
        }
        appUser.setStatus(enabled ? CommonConstant.USER_ENABLE : CommonConstant.USER_DISENABLE);
        appUser.setUpdateTime(new Date());

        int i = jsptUserService.getBaseMapper().updateById(appUser);
        log.info("修改用户：{}", appUser);

        return i > 0 ? Result.succeed(appUser, "更新成功") : Result.failed("更新失败");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result saveOrUpdateUser(JsptUser jsptUser) throws Exception {
        if (jsptUser.getId() == null) {
            if (StringUtils.isBlank(jsptUser.getTenantId())) {
                jsptUser.setTenantId(UserType.BACKEND.name());
            }
            jsptUser.setPassword(passwordEncoder.encode(CommonConstant.DEF_USER_PASSWORD));
            //设置枚举值，启用为0， 禁用为1
            jsptUser.setStatus(CommonConstant.USER_ENABLE);
        }
        String username = jsptUser.getUsername();
        boolean result = jsptUserService.saveOrUpdateIdempotency(jsptUser, lock
                , LOCK_KEY_USERNAME + username, new QueryWrapper<JsptUser>().eq("username", username)
                , username + "已存在");
        //更新角色
        if (result && StrUtil.isNotEmpty(jsptUser.getRoleId())) {
            jsptRoleUserService.deleteUserRole(jsptUser.getId(), null);
            List roleIds = Arrays.asList(jsptUser.getRoleId().split(","));
            if (!CollectionUtils.isEmpty(roleIds)) {
                List<JsptRoleUser> roleUsers = new ArrayList<>(roleIds.size());
                roleIds.forEach(roleId -> roleUsers.add(new JsptRoleUser(jsptUser.getId(), Long.parseLong(roleId.toString()))));
                jsptRoleUserService.saveBatch(roleUsers);
            }
        }
        return result ? Result.succeed(jsptUser, "操作成功") : Result.failed("操作失败");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean delUser(Long id) {
        jsptRoleUserService.deleteUserRole(id, null);
        return jsptUserService.getBaseMapper().deleteById(id) > 0;
    }

    @Override
    public List<JsptUserExcel> findAllUsers(Map<String, Object> params) {
        List<JsptUserExcel> jsptUserExcels = new ArrayList<>();
        // List<JsptUser> list = jsptUserService.getBaseMapper().findList(new Page<>(1, -1), params);
        List<JsptUser> list = jsptUserService.list();

        for (JsptUser jsptUser : list) {
            JsptUserExcel jsptUserExcel = new JsptUserExcel();
            BeanUtils.copyProperties(jsptUser, jsptUserExcel);
            jsptUserExcels.add(jsptUserExcel);
        }
        return jsptUserExcels;
    }

    @Override
    public JsptUser findUserById(long id) {
        return jsptUserService.getById(id);
    }

    @Override
    public void updateSysUser(JsptUser jsptUser) {
        jsptUserService.updateById(jsptUser);
    }

    @Override
    public void saveBatch(List<JsptUser> users) {
        jsptUserService.saveBatch(users);
    }
}
